from django.conf import settings
from django.core.mail import send_mail
from django.shortcuts import render
from django.views import View
from apps.users.models import User
from django import http
import logging,json,re
from django.contrib.auth import login,authenticate,logout
from django_redis import get_redis_connection
from apps.users.models import User
from meiduo_mall.utils.views import LoginRequiredJSONMixin
from celery_tasks.email.tasks import send_email_verify_url
from apps.users.utils import generate_email_verify_url,check_email_verify_url

# Create your views here.

# 日志输出器
logger = logging.getLogger('django')


class EmailActiveView(View):
    '''验证激活邮箱
    PUT /emails/verification/
    '''
    def put(self,request):
        '''实现验证激活邮箱逻辑'''
        # 接收参数
        token = request.GET.get('token')
        # 校验参数
        if not token:
            return http.JsonResponse({'code':400,'errmsg':'缺少token'})

        # 实现核心逻辑
        # 通过token提取要验证的用户
        user = check_email_verify_url(token=token)

        # 将要验证邮箱的用户的email_active字段设置为True
        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':400,'errmsg':'邮箱验证失败'})

        # 响应结果
        return http.JsonResponse({'code':0,'errmsg':'邮箱验证成功'})
class ChangePasswordView(View):
    '''修改密码
    PUT /password/
    '''
    def put(self,request):
        '''修改密码逻辑'''
        # 接收参数
        json_dict = json.loads(request.body.decode())
        old_password = json_dict.get('old_password')
        new_password = json_dict.get('new_password')
        new_password2 = json_dict.get('new_password2')
        # 校验参数
        if not all([old_password,new_password,new_password2]):
            return http.JsonResponse({'code':400,'errmsg':'缺少必传参数'})
        result = request.user.check_password(old_password)
        # 判断原始密码是否正确
        if not result:
            return http.JsonResponse({'code':400,'errmsg':'原始密码不正确'})
        # 判断新密码是否满足格式
        if not re.match(r'^[0-9a-zA-Z]{8,20}$',new_password):
            return http.JsonResponse({'code':400,'errmsg':'新密码格式不对'})

        # 判断第二次密码是否与第一次密码一致
        if not new_password != new_password2:
            return http.JsonResponse({'code':400,'errmsg':'两次密码不一致'})
        #
        # 修改密码
        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':400,'errmsg':'修改密码失败'})
        # 清理状态保持信息
        logout(request)
        response = http.JsonResponse({'code':0,'errmsg':'ok'})
        response.delete_cookie('username')
        # 响应密码修改结果，重定向到登录页面
        return response


class EmailView(LoginRequiredJSONMixin,View):
    '''添加邮箱
    PUT /emails/
    '''
    def put(self,request):
        '''实现添加邮箱的逻辑'''
        # 接收参数
        json_dict = json.loads(request.body.decode())
        email = json_dict.get('email')
        # 校验参数
        if not email:
            return http.JsonResponse({'code':400,'errmsg':'缺少必传参数'})
        # 校验邮箱格式
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$',email):
            return http.JsonResponse({'code':400,'errmsg':'参数email格式错误'})
        # 实现对应的核心逻辑:将用户填写的邮箱地址保存到当前登录用户的email字段
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':400,'errmsg':'添加邮箱失败'})

        # 发送邮件的验证激活邮件:耗时操作，不能让它阻塞主逻辑，主要从主逻辑中解耦出来
        verify_url = generate_email_verify_url(user=request.user)
        # print(email)
        send_email_verify_url.delay(email,verify_url)


        # 响应结果
        return http.JsonResponse({'code':0,'errmsg':'ok'})


class UserInfoView(LoginRequiredJSONMixin, View):
    '''用户中心
    GET /info/
    '''

    def get(self,request):
        '''实现用户中心基本信息展示
        由于我们在该接口中，判断了用户是否是登录用户
        所以能够进入到该接口的请求，一定是登录用户发送的
        所以request.user里面获取的信息一定是当前登录用户的信息
        重要技巧：
            如果接口只有登录用户可以访问，那么在接口内
        '''
        data_dict = {
            'code': 0,
             'errmsg': 'ok',
             'info_data': {
                 'username': request.user.username,
                 'mobile': request.user.mobile,
                 'email': request.user.email,
                 'email_active': request.user.email_active
             }
        }
        return http.JsonResponse(data_dict)
# class UserInfoView(View):
#     '''用户中心
#     GET /info/
#     '''
#
#     def get(self,request):
#         '''实现用户中心数据展示
#         '''
#         # 使用is_authenticated属性贩毒案是否登录
#         if not request.user.is_authenticated:
#             # False 未登录
#             return http.JsonResponse({'code': 400, 'errmsg': '用户未登录'})
#
#         # True已登录
#         data_dict = {
#             'code': 0,
#             'errmsg': 'ok',
#             'info_data': {
#                 'username': '',
#                 'mobile': '',
#                 'email': '',
#                 'email_active': ''
#             }
#         }
#
#         return http.JsonResponse(data_dict)


class LogoutView(View):
    '''退出登录
    DELETE /logout/
    '''

    def delete(self,request):
        '''实现退出登录逻辑
        提示:正好跟登录逻辑相反
            如果登陆成功后,记住登录状态,
            那么退出登录就是清理登录状态
            如果登陆成功后,需要将用户名写到cookie
            退出就是清理用户名cookie
        '''
        # 清理登录状态
        logout(request)

        # 清理用户名cookie
        response = http.JsonResponse({'code':0,'errmsg':'退出登录成功'})
        response.delete_cookie('username')
        return response


class LoginView(View):
    '''用户登录
    GET /login/
    '''
    def post(self,request):
        '''实现用户登录逻辑'''
        #　接收参数
        json_dict = json.loads(request.body.decode())
        # 该参数既可以是用户名也可以是手机号
        account = json_dict.get('username')
        password = json_dict.get('password')
        # True 、False可真可假
        remembered = json_dict.get('remembered')

        # 校验参数
        if not all([account, password]):
            return http.JsonResponse({'code':400,'errmsg':'缺少必传参数'})
        # if not re.match(r'^[a-zA-Z0-9_-]{5,20}$',account):
        #     return http.JsonResponse({'code':400,'errmsg':'参数username有误'})
        if not re.match(r'^[0-9a-zA-Z]{8,20}$',password):
            return http.JsonResponse({'code':400,'errmsg':'参数password有误'})

        # 实现多账号登录
        # 判断用户输入的是用户名还是是手机号
        if re.match(r'^1[3-9]\d{9}$',account):
            # 用户输入的账号是手机号码:将USERNAME_FIELD指定为mobile
            User.USERNAME_FIELD = 'mobile'
        else:
            # 用户输入的账号是用户名:将USERNAME_FILED指定为username
            User.USERNAME_FIELD = 'username'
        # 实现核心逻辑
        # 认证登录用户
        # 核心思想：先使用用户名作为条件去用户表查询是否存在，如果存在再校验密码书否正确
        # 认证登录用户： Django的用户认证系统已经封装好这个逻辑
        user = authenticate(request=request,username=account,password=password)

        # 判断用户认证是否成功
        if not user:
            return http.JsonResponse({'code':400,'errmsg':'用户名或密码错误'})

        # 实现状态保持
        login(request,user)
        # 还需要根据remembered参数去设置状态保持周期
        # 如果用户选择了记住登录，那么状态保持的周期为两周
        # 反之，浏览器回话结束状态保持就撤销
        if remembered:
            # 记住登录:状态保持周期为两周（设置session数据的过期时间）
            # set_expiry(None):Django封装好的，默认两周
            request.session.set_expiry(None)

        else:
            # 没有记住:浏览器会话结束保持状态就销毁
            request.session.set_expiry(0)
        # 登录成功后,将用户名写入到cookie展示在右上角
        response = http.JsonResponse({'code': 0, 'errmsg': '注册成功'})
        response.set_cookie('username', user.username, max_age=14 * 24 * 3600)

        # 响应结果
        return response



class RegisterView(View):
    '''用户注册
    POST http://www.meiduo.site:8000/register/
    '''
    def post(self, request):
        '''实现逻辑'''
        # 接受参数 请求体中的JSON数据 request.body
        json_org = request.body # 从请求体中获取原始的JSON数据bytes类型
        json_str = json_org.decode() # 将bytes类型的JSON数据转成JSON字符串
        json_dict = json.loads(json_str) # 将JSON字符串转成Python标准字典

        # 简写 json_dict = json.loads(request.body.decode())

        # 提取参数
        username = json_dict.get('username')
        password = json_dict.get('password')
        password2 = json_dict.get('password2')
        mobile = json_dict.get('mobile')
        # 提取短信验证码的参数
        sms_code_client = json_dict.get('sms_code')
        allow = json_dict.get('allow')

        # 校验参数
        # 判断是否缺少必传参数
        # all() 判断某些数据中是否有为空数据
        if not all([username, password, password2, mobile,sms_code_client, allow]):
            # 如果缺少必传参数就返回400状态码和错误信息立马终止逻辑
            return http.JsonResponse({'code':400,'errmsg':'缺少必传参数'})
        # 判断用户名是否满足格式要求
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$',username):
            return http.JsonResponse({'code':400,'errmsg':'参数username有误'})
        # 判断密码是否满足格式要求
        if not re.match(r'^[0-9a-zA-Z]{8,20}$',password):
            return http.JsonResponse({'code': 400, 'errmsg': '参数password有误'})
        # 判断两次密码是否一致
        if password != password2:
            return http.JsonResponse({'code': 400, 'errmsg': '两次密码不一致'})
        # 判断手机号码是否满足格式要求
        if not re.match(r'^1[3-9]\d{9}$',mobile):
            return http.JsonResponse({'code': 400, 'errmsg': '手机号传输有误'})

        # 追加短信验证码逻辑
        # 提取服务端储存的短信验证码：以前怎么存储现在怎么提取
        redis_conn = get_redis_connection('verify_code')
        sms_code_server = redis_conn.get('sms_%s' % mobile)

        # 判断短信验证码是否过期
        if not sms_code_server:
            return http.JsonResponse({'code':400,'errmsg':'短信验证码失效'})
        # 判断短信验证码是否和服务端一致
        if sms_code_client != sms_code_server.decode():
            return http.JsonResponse({'code':400,'errmsg':'短信验证码有误'})

        # 判断是否勾选协议
        if allow != True:
            return http.JsonResponse({'code': 400, 'errmsg': '参数allow有误'})

        # 实现核心逻辑： 保存注册数据到用户数据表
        # 由于美多商城的用户模块完全依赖于Django自带的用户模型类
        # 所以用户相关的一切操作都需要去调用Django自带的用户模型类提供的方法和属性
        # 其中就包括用户注册数据
        # create_user()方法专门保存注册数据
        try:
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':400,'errmsg':'注册失败'})

        # 实现状态保持： 因为美多商城的需求是注册成功及登陆成功
        # 我们要记住当前用户登陆过，cookie机制，session机制（选择）
        # 如何证明但前的用户登陆过选择session机制，记住登录状态校验登录状态

        # login()方法是Django提供用于实现登录注册状态保持
        # login('请求对象','注册后或登录认证后的用户')
        login(request,user)

        # 在用户注册成功之后,将用户写入cookie将来会在页面右上角展示
        response = http.JsonResponse({'code':0,'errmsg':'注册成功'})
        response.set_cookie('username',user.username,max_age=14*24*3600)

        # 响应结果
        return response

class MobileConverter(View):
    '''判断手机号是否重复注册

    '''
    def get(self,request,mobile):
        '''查询手机号对应的个数'''
        try:
            count = User.objects.filter(mobile=mobile).count()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':400,'errmsg':'信息错误'})
        return http.JsonResponse({'code':0,'errmsg':'ok','count':count})


class UsernameCountView(View):
    '''判断用户名是否重复注册
    GET http://www.meiduo.site:8000/usernames/itcast/count/
    '''
    def get(self,request,username):
        '''查询用户名对应的记录的个数'''
        try:
            count = User.objects.filter(username=username).count()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':400, 'errmsg':'错误信息'})

        return http.JsonResponse({'code':'0', 'errmsg':'OK','count': count})
